package com.ly.hotel.util.bugtrace;

import java.io.BufferedReader;  
import java.io.File;  
import java.io.FileInputStream;  
import java.io.FileNotFoundException;  
import java.io.FileOutputStream;  
import java.io.IOException;  
import java.io.InputStreamReader;  
import java.io.PrintWriter;  
import java.io.StringWriter;  
import java.io.Writer;  
import java.lang.Thread.UncaughtExceptionHandler;  
import java.lang.reflect.Field;  
import java.text.DateFormat;  
import java.text.SimpleDateFormat;  
import java.util.Date;  
import java.util.HashMap;  
import java.util.Map;  

import org.apache.http.Header;

import com.loopj.android.http.AsyncHttpResponseHandler;
import com.ly.hotel.common.MyApplication;
import com.ly.hotel.common.MyLogsManager;
import com.ly.hotel.service.MyCloudBackService;
import com.ly.hotel.util.DateUtils;
import com.ly.hotel.util.HttpHelper;

import android.annotation.SuppressLint;
import android.content.Context;  
import android.content.pm.PackageInfo;  
import android.content.pm.PackageManager;  
import android.content.pm.PackageManager.NameNotFoundException;  
import android.os.Build;  
import android.os.Environment;  
import android.os.Looper;  
import android.util.Log;  
import android.widget.Toast;  
     
/**   
* UncaughtException处理类,当程序发生Uncaught异常的时候,有该类来接管程序,并记录发送错误报告.  
*  
*  需要在Application中注册，为了要在程序启动器就监控整个程序。 
*/      
public class CrashHandler implements UncaughtExceptionHandler {      
         
   public static final String TAG = "CrashHandler";      
         
   //系统默认的UncaughtException处理类       
   private UncaughtExceptionHandler mDefaultHandler;
   //CrashHandler实例      
   private static CrashHandler instance;  
  //程序的Context对象      
   private Context mContext;      
   //用来存储设备信息和异常信息      
   private Map<String, String> infos = new HashMap<String, String>();      
     
   //用于格式化日期,作为日志文件名的一部分      
   @SuppressLint("SimpleDateFormat")
   private DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");      
   public static boolean rebootNoLog = false;  
   /** 保证只有一个CrashHandler实例 */      
   private CrashHandler() {}      
     
   /** 获取CrashHandler实例 ,单例模式 */      
   public static CrashHandler getInstance() {      
       if(instance == null)  
           instance = new CrashHandler();     
       return instance;      
   }      
     
   /**   
    * 初始化   
    */      
   public void init(Context context) {      
       mContext = context;      
       //获取系统默认的UncaughtException处理器      
       mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();      
       //设置该CrashHandler为程序的默认处理器      
       Thread.setDefaultUncaughtExceptionHandler(this);      
   }      
     
   /**   
    * 当UncaughtException发生时会转入该函数来处理   
    */      
   @Override      
   public void uncaughtException(Thread thread, Throwable ex) {      
       if (!handleException(ex) && mDefaultHandler != null) {      
           //如果用户没有处理则让系统默认的异常处理器来处理      
           mDefaultHandler.uncaughtException(thread, ex);      
       } else {      
           try {      
               Thread.sleep(3000);      
           } catch (InterruptedException e) {      
               Log.e(TAG, "error : ", e);      
           }      
           //退出程序      
           android.os.Process.killProcess(android.os.Process.myPid());      
           System.exit(1);      
       }      
   }      
     
   public void setHandlerExceptionSendtoCloud(){
	   Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler() {//给主线程设置一个处理运行时异常的handler 
      	 
           @Override 
           public void uncaughtException(Thread thread, final Throwable ex) { 
        	   if(rebootNoLog){
        		 //等待一会，就重启之
	               try {      
	                   Thread.sleep(2000);      
	                   android.os.Process.killProcess(android.os.Process.myPid());      
	                   //退出程序      
	                   System.exit(0);      
	               } catch (InterruptedException e) {      
	                   Log.e(TAG, "error : ", e);      
	               }      
        	   } else {
	               StringWriter sw = new StringWriter(); 
	               PrintWriter pw = new PrintWriter(sw); 
	               ex.printStackTrace(pw); 
	                
	               StringBuilder sb = new StringBuilder(); 
	                
	               sb.append("Board Version code is "); 
	               sb.append(Build.VERSION.SDK_INT + "%09");//设备的Android版本号 
	               
	               if(MyApplication.getApkVersion() != null){
	            	   sb.append("Spa Version "); 
	            	   sb.append(MyApplication.getApkVersion() + "%09");//设备的Android版本号 
	               }
	               
	               sb.append("Model is "); 
	               sb.append(Build.MODEL + "%09");//设备型号 
	               char b = 0x09;
	               char c = 0x20;
	               
	               //要去掉一下特殊字符，否则通过http上传数据会出错，这些只是目前发现的，如果出现没有覆盖的字符，就会导致无法上传
	               sb.append(sw.toString().replace(b, c).replace("\n", "%09").replace("/", "%2f").replace("{", "%7b").replace("}", "%7d")); 
	               
	               Log.i("bugtrace", sb.toString());
	               /*SendLogEvent loge = new SendLogEvent(MyLogsManager.CLOUD_LOGS_BUGS_REPORT_CRASH + sb.toString());
	               loge.noDec = true;
	               EventBus.getDefault().post(loge);*/
	               
	               /*时间紧急，直接发送！！上传到服务器*/
	               MyApplication app = (MyApplication) MyApplication.getContext();
	               String url = "http://" + MyCloudBackService.cloudLogsServerIp + ":"+ MyCloudBackService.cloudLogsServerPort 
	            		   + "/log?content=" + DateUtils.formatToLong(new Date()) + "%09"  + app.getAuthToCompany() 
							+ "%09" + MyApplication.getMAC() + "%09" + MyApplication.getDeviceInfo().getCategoryName() 
							+ "-" +MyApplication.getDeviceInfo().getName()+ "%09" + MyLogsManager.CLOUD_LOGS_BUGS_REPORT_CRASH + sb.toString();
					HttpHelper.myGetStringAsync(mContext, url, null, new AsyncHttpResponseHandler() {
						
						@Override
						public void onSuccess(int arg0, Header[] arg1, byte[] arg2) {
							// TODO Auto-generated method stub
							Log.i("logs", "奔溃日志处理完成:" + new String(arg2));
						}
						
						@Override
						public void onFailure(int arg0, Header[] arg1, byte[] arg2, Throwable arg3) {
							// TODO Auto-generated method stub
							Log.i("logs", "奔溃日志处理失败");
						}
					},MyCloudBackService.isNeedhttpProxy);
	               /**/
	               
	               //顺便输出到logcat
	               ex.printStackTrace();
	               
	               //等待一会，就重启之
	               try {      
	                   Thread.sleep(5000);      
	                   android.os.Process.killProcess(android.os.Process.myPid());      
	                   //退出程序      
	                   System.exit(0);      
	               } catch (InterruptedException e) {      
	                   Log.e(TAG, "error : ", e);      
	               }      
	               
	           } 
           }
       }); 
   }
   
   /**   
    * 自定义错误处理,收集错误信息 发送错误报告等操作均在此完成.   
    *    
    * @param ex   
    * @return true:如果处理了该异常信息;否则返回false.   
    */      
   private boolean handleException(Throwable ex) {      
       if (ex == null) {      
           return false;      
       }      
       //收集设备参数信息       
       collectDeviceInfo(mContext);      
         
       //使用Toast来显示异常信息      
       new Thread() {      
           @Override      
           public void run() {      
               Looper.prepare();      
               Toast.makeText(mContext, "很抱歉,程序出现异常,即将退出.", Toast.LENGTH_SHORT).show();      
               Looper.loop();      
           }      
       }.start();      
       //保存日志文件       
//       saveCatchInfo2File(ex);    
       return true;      
   }      
         
   /**   
    * 收集设备参数信息   
    * @param ctx   
    */      
   public void collectDeviceInfo(Context ctx) {      
       try {      
           PackageManager pm = ctx.getPackageManager();      
           PackageInfo pi = pm.getPackageInfo(ctx.getPackageName(), PackageManager.GET_ACTIVITIES);      
           if (pi != null) {      
               String versionName = pi.versionName == null ? "null" : pi.versionName;      
               String versionCode = pi.versionCode + "";      
               infos.put("versionName", versionName);      
               infos.put("versionCode", versionCode);      
           }      
       } catch (NameNotFoundException e) {      
           Log.e(TAG, "an error occured when collect package info", e);      
       }      
       Field[] fields = Build.class.getDeclaredFields();      
       for (Field field : fields) {      
           try {      
               field.setAccessible(true);      
               infos.put(field.getName(), field.get(null).toString());      
               Log.d(TAG, field.getName() + " : " + field.get(null));      
           } catch (Exception e) {      
               Log.e(TAG, "an error occured when collect crash info", e);      
           }      
       }      
   }      
     
   /**   
    * 保存错误信息到文件中   
    *    
    * @param ex   
    * @return  返回文件名称,便于将文件传送到服务器   
    */      
   private String saveCatchInfo2File(Throwable ex) {      
             
       StringBuffer sb = new StringBuffer();      
       for (Map.Entry<String, String> entry : infos.entrySet()) {      
           String key = entry.getKey();      
           String value = entry.getValue();      
           sb.append(key + "=" + value + "\n");      
       }      
             
       Writer writer = new StringWriter();      
       PrintWriter printWriter = new PrintWriter(writer);      
       ex.printStackTrace(printWriter);      
       Throwable cause = ex.getCause();      
       while (cause != null) {      
           cause.printStackTrace(printWriter);      
           cause = cause.getCause();      
       }      
       printWriter.close();      
       String result = writer.toString();      
       sb.append(result);      
       try {      
           long timestamp = System.currentTimeMillis();      
           String time = formatter.format(new Date());      
           String fileName = "crash-" + time + "-" + timestamp + ".log";      
           if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {      
               String path = "/mnt/sdcard/crash/";      
               File dir = new File(path);      
               if (!dir.exists()) {      
                   dir.mkdirs();      
               }      
               FileOutputStream fos = new FileOutputStream(path + fileName);      
               fos.write(sb.toString().getBytes());    
               //发送给开发人员  
               sendCrashLog2PM(path+fileName);  
               fos.close();      
           }      
           return fileName;      
       } catch (Exception e) {      
           Log.e(TAG, "an error occured while writing file...", e);      
       }      
       return null;      
   }      
     
   /** 
    * 将捕获的导致崩溃的错误信息发送给开发人员 
    *  
    * 目前只将log日志保存在sdcard 和输出到LogCat中，并未发送给后台。 
    */  
   private void sendCrashLog2PM(String fileName){  
       if(!new File(fileName).exists()){  
           Toast.makeText(mContext, "日志文件不存在！", Toast.LENGTH_SHORT).show();  
           return;  
       }  
       FileInputStream fis = null;  
       BufferedReader reader = null;  
       String s = null;  
       try {  
           fis = new FileInputStream(fileName);  
           reader = new BufferedReader(new InputStreamReader(fis, "GBK"));  
           while(true){  
               s = reader.readLine();  
               if(s == null) break;  
               //由于目前尚未确定以何种方式发送，所以先打出log日志。  
               Log.i("info", s.toString());  
           }  
       } catch (FileNotFoundException e) {  
           e.printStackTrace();  
       } catch (IOException e) {  
           e.printStackTrace();  
       }finally{   // 关闭流  
           try {  
               reader.close();  
               fis.close();  
           } catch (IOException e) {  
               e.printStackTrace();  
           }  
       }  
   }  
}     